using System;
using System.IO;
using Communication;
using Storage.Core;

namespace Storage.Server
{
	internal class StorageServerHandler : IRequestHandler, IDisposable
	{
		public StorageServerHandler(IStorage storage)
		{
			this.storage = storage;
		}

		public void Handle(IRequestContext context)
		{
			using (context.OutputStream)
			{
				var deserializer = new FastBinaryDeserializer(context.Request.GetBuffer(), 0);
				var response = ParseAndProcessQuery(deserializer);
				WriteResponse(context.OutputStream, response.Serialize());
			}
		}

		public void Dispose()
		{
			storage.Dispose();
		}

		private IStorageResponse ParseAndProcessQuery(FastBinaryDeserializer deserializer)
		{
			try
			{
				switch (deserializer.ReadByte())
				{
					case StorageOpCodes.AddOrUpdate:
						return AddOrUpdate(AddOrUpdateQuery.Deserialize(deserializer));
					case StorageOpCodes.ReadById:
						return ReadById(ReadByIdQuery.Deserialize(deserializer));
					case StorageOpCodes.ReadByType:
						return ReadByType(ReadByTypeQuery.Deserialize(deserializer));
					case StorageOpCodes.Delete:
						return Delete(DeleteQuery.Deserialize(deserializer));
					default: return new ErrorResponse("Unknown operation code.");
				}
			}
			catch (Exception error)
			{
				return new ErrorResponse(error.ToString());
			}
		}

		private AddOrUpdateResponse AddOrUpdate(AddOrUpdateQuery query)
		{
			return new AddOrUpdateResponse(storage.AddOrUpdate(query.Entity));
		}

		private ReadByIdResponse ReadById(ReadByIdQuery query)
		{
			return new ReadByIdResponse(storage.ReadById(query.Id));
		}

		private ReadByTypeResponse ReadByType(ReadByTypeQuery query)
		{
			return new ReadByTypeResponse(storage.ReadByType(query.Type));
		}

		private DeleteResponse Delete(DeleteQuery query)
		{
			return new DeleteResponse(storage.Delete(query.Id));
		}

		private static void WriteResponse(Stream clientStream, byte[] response)
		{
			using (clientStream)
				clientStream.Write(response, 0, response.Length);
		}

		private readonly IStorage storage;
	}
}